import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

// 论文类型枚举
enum PaperType {
    Thesis,          // 学位论文
    JournalPaper,    // 期刊论文
    ConferencePaper, // 会议论文
    Any              // 任意类型
}

/**
 * 论文基类（不可变类）
 */
abstract class Paper {
    private final String title;

    public Paper(String title) {
        if (title == null || title.length() < 3) {
            throw new IllegalArgumentException("论文标题长度至少为3个字符");
        }
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName() + "(" + title + ")";
    }
}

/**
 * 学位论文
 */
class Thesis extends Paper {
    public Thesis(String title) {
        super(title);
    }
}

/**
 * 期刊论文
 */
class JournalPaper extends Paper {
    public JournalPaper(String title) {
        super(title);
    }
}

/**
 * 会议论文
 */
class ConferencePaper extends Paper {
    public ConferencePaper(String title) {
        super(title);
    }
}

/**
 * 学生类（不可变类）
 */
class Student {
    private final String name;
    private final int preferredQuantity;
    private final PaperType preferredType;

    public Student(String name, int preferredQuantity, PaperType preferredType) {
        if (name == null || preferredQuantity < 1 || preferredQuantity > 4) {
            throw new IllegalArgumentException("学生信息不合法");
        }
        this.name = name;
        this.preferredQuantity = preferredQuantity;
        this.preferredType = preferredType;
    }

    public String getName() {
        return name;
    }

    public int getPreferredQuantity() {
        return preferredQuantity;
    }

    public PaperType getPreferredPaperType() {
        return preferredType;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", preferredQuantity=" + preferredQuantity +
                ", preferredType=" + preferredType +
                '}';
    }
}

/**
 * 论文选择结果类
 */
class PaperSelection {
    private final Map<Paper, Student> selections;

    public PaperSelection() {
        selections = new HashMap<>();
    }

    public boolean addSelection(Student student, Paper paper) {
        if (student == null || paper == null) {
            return false;
        }
        selections.put(paper, student);
        return true;
    }

    public boolean isSelected(Paper paper) {
        return selections.containsKey(paper);
    }

    public Map<Paper, Student> getSelections() {
        return new HashMap<>(selections);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Paper p : selections.keySet()) {
            sb.append(p).append("\t selected by ").append(selections.get(p).getName()).append("\n");
        }
        return sb.toString();
    }

    public void accept(PaperSelectionVisitor visitor) {
    }
}

/**
 * 论文选择策略接口（策略模式）
 */
interface PaperSelectionStrategy {
    List<Paper> selectPapers(List<Paper> availablePapers, int count);
}

/**
 * 随机选择策略
 */
class RandomSelectionStrategy implements PaperSelectionStrategy {
    @Override
    public List<Paper> selectPapers(List<Paper> availablePapers, int count) {
        if (availablePapers == null || availablePapers.isEmpty() || count <= 0) {
            return Collections.emptyList();
        }
        int selectCount = Math.min(count, availablePapers.size());
        List<Paper> result = new ArrayList<>();
        Set<Integer> selectedIndices = new HashSet<>();

        while (result.size() < selectCount) {
            int index = ThreadLocalRandom.current().nextInt(0, availablePapers.size());
            if (!selectedIndices.contains(index)) {
                selectedIndices.add(index);
                result.add(availablePapers.get(index));
            }
        }
        return result;
    }
}

/**
 * 顺序选择策略
 */
class OrderSelectionStrategy implements PaperSelectionStrategy {
    @Override
    public List<Paper> selectPapers(List<Paper> availablePapers, int count) {
        if (availablePapers == null || availablePapers.isEmpty() || count <= 0) {
            return Collections.emptyList();
        }
        int selectCount = Math.min(count, availablePapers.size());
        return new ArrayList<>(availablePapers.subList(0, selectCount));
    }
}

/**
 * 论文列表类
 */
class PaperList {
    private final String teacher;
    private final List<Paper> papers;
    private final PaperSelection selections;
    private PaperSelectionStrategy strategy;

    public PaperList(String teacher, List<Paper> papers) {
        this.teacher = teacher;
        this.papers = new ArrayList<>(papers);
        this.selections = new PaperSelection();
        this.strategy = new RandomSelectionStrategy(); // 默认使用随机策略
    }

    // 设置选择策略
    public void setStrategy(PaperSelectionStrategy strategy) {
        this.strategy = Objects.requireNonNull(strategy, "策略不能为null");
    }

    // 根据类型获取未被选择的论文
    private List<Paper> getUnselectedPapersByType(PaperType type) {
        List<Paper> result = new ArrayList<>();
        for (Paper p : papers) {
            if (!selections.isSelected(p)) {
                if (type == PaperType.Any || type.toString().equals(p.getClass().getSimpleName())) {
                    result.add(p);
                }
            }
        }
        return result;
    }

    // 自定义异常：论文数量不足
    public static class NoEnoughPapersException extends Exception {
        private final String studentName;
        private final int expectedCount;
        private final int availableCount;

        public NoEnoughPapersException(String studentName, int expectedCount, int availableCount) {
            this.studentName = studentName;
            this.expectedCount = expectedCount;
            this.availableCount = availableCount;
        }

        @Override
        public String getMessage() {
            return "学生 " + studentName + " 希望选论文 " + expectedCount + " 篇，但目前只有 " + availableCount + " 篇可选";
        }
    }

    // 根据学生偏好选择论文
    public List<Paper> selectPaperByRandom(Student student) throws NoEnoughPapersException {
        if (student == null) {
            throw new IllegalArgumentException("学生对象不能为null");
        }

        List<Paper> availablePapers = getUnselectedPapersByType(student.getPreferredPaperType());
        int availableCount = availablePapers.size();
        int expectedCount = student.getPreferredQuantity();

        if (availableCount < expectedCount) {
            throw new NoEnoughPapersException(
                    student.getName(), expectedCount, availableCount);
        }

        List<Paper> selectedPapers = strategy.selectPapers(availablePapers, expectedCount);
        for (Paper paper : selectedPapers) {
            selections.addSelection(student, paper);
        }
        return selectedPapers;
    }

    // 获取选择结果
    public PaperSelection getSelectionResult() {
        return selections;
    }
}

/**
 * 访问者接口（访问者模式）
 */
interface PaperSelectionVisitor {
    void visit(PaperSelection paperSelection);

    int getStudentCount();
}

/**
 * 学生数量统计访问者
 */
class StudentCountVisitor implements PaperSelectionVisitor {
    private int studentCount;

    public int getStudentCount() {
        return studentCount;
    }

    @Override
    public void visit(PaperSelection paperSelection) {
        studentCount = new HashSet<>(paperSelection.getSelections().values()).size();
    }
}

/**
 * 客户端主程序
 */
class Client {
    public static void main(String[] args) {
        // 创建论文列表
        List<Paper> papers = Arrays.asList(
                new Thesis("Reading"),
                new ConferencePaper("Science"),
                new JournalPaper("Technology"),
                new JournalPaper("Art")
        );
        PaperList listOfRainy = new PaperList("Rainy", papers);

        // 创建学生列表
        List<Student> students = Arrays.asList(
                new Student("Cloudy", 1, PaperType.Thesis),
                new Student("Sunny", 1, PaperType.JournalPaper),
                new Student("Windy", 2, PaperType.Any),
                new Student("Snowy", 3, PaperType.Any)
        );

        // 演示策略模式：切换为顺序选择策略
        listOfRainy.setStrategy(new OrderSelectionStrategy());

        // 学生选择论文（模拟并发）
        for (Student student : students) {
            new Thread(() -> {
                try {
                    System.out.println("学生 " + student.getName() + " 开始选择论文...");
                    List<Paper> selected = listOfRainy.selectPaperByRandom(student);
                    if (!selected.isEmpty()) {
                        System.out.println(student.getName() + " 成功选择论文: " + selected);
                    }
                } catch (PaperList.NoEnoughPapersException e) {
                    System.out.println(e.getMessage());
                } catch (Exception e) {
                    System.err.println("选择过程出错: " + e.getMessage());
                }
            }).start();

            // 等待线程执行，模拟并发冲突
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 等待所有线程完成
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 输出最终选择结果
        System.out.println("\n===== 最终论文选择结果 =====");
        System.out.println(listOfRainy.getSelectionResult());

        // 使用访问者模式统计选择人数
        PaperSelectionVisitor visitor = new StudentCountVisitor();
        listOfRainy.getSelectionResult().accept(visitor);
        System.out.println("共有 " + visitor.getStudentCount() + " 名学生成功选择了论文");
    }
}